/*
 * ADDriver.c
 *
 *  Created on: Oct 28, 2011
 *      Author: Christpher Wood, Max Macri
 */

// Module includes
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <sys/siginfo.h>
#include <sys/netmgr.h>
#include <sys/neutrino.h>
#include <hw/inout.h>
#include "Converter.h"
#include "types.h"

// Constants used by the ADDriver
#define DIO_CTRL_ADDRESS (0x28B)
#define PORTA_ADDRESS (0x288)
#define PORTB_ADDRESS (0x289)
#define PORTC_ADDRESS (0x28A)
#define DATA_ADDRESS (0x378) // Data register address
#define CTRL_ADDRESS (0x37a) // Control register address
#define STUS_ADDRESS (0x379) // Status register address
#define INIT_BIT (0x04) // Init bit for the paralle port (pin 2)
#define INTR_BIT (0x10) // Interrupt bit for the paralle port (pin 4)
#define PARALLEL_IRQ (0x07) // Parallel port IRQ
#define LOW (0x00) // Low signal value for parallel port
#define HIGH (0xFF) // Low signal value for parallel port
#define TIMEOUT_ROLLOVER (4) // The timeout rollover
#define BYTE_PATTERN (0x19)
#define ONE_SECOND (1000000000)
#define MIN_VOLTAGE_VAL (-5)
#define MAX_VOLTAGE_VAL (5)
#define CONVERSION_FACTOR (25.4) // -127 to 127 for servo positions

// Enumeration for the various states the system can be in
typedef enum
{
	COMM_STATE_UP = 0x01,
	COMM_STATE_DOWN = 0x02,
	VOLTAGE_RANGE_WARNING = 0x04
} SystemState;

// The main entry point into the program.
//--------------------------------------------------------------     
int main(int argc, char *argv[])
{
	INT16 adReturnValue;
	float voltageLevel;
	INT8 privityErr;
	uintptr_t ppCtrlHandle;
	uintptr_t ppDataHandle;
	uintptr_t ppStatusHandle;
	uintptr_t dioControlHandle;
	uintptr_t adDataHandle;
	uintptr_t doneBitHandle;
	uintptr_t commStatusHandle;
	struct sigevent event;
	INT8 intrId;
	struct sigevent timerEvent;
	uint64_t timeoutSetTime;
	uint64_t timeoutRemTime;

	// Print startup banner
	printf("------------------------------------\n");
	printf("Analog-to-Digital Conversion Program\n");
	printf("------------------------------------\n");

	// Configure the parallel printer port
	privityErr = ThreadCtl(_NTO_TCTL_IO, NULL );
	if (privityErr == -1)
	{
		printf("Can't get root permissions\n");
	}

	// Get a handle to the parallel port's control register
	ppCtrlHandle = mmap_device_io(PORT_LENGTH, CTRL_ADDRESS );

	// Initialize the parallel port
	out8(ppCtrlHandle, INIT_BIT );

	// Enable interrupts on the parallel port
	out8(ppCtrlHandle, INTR_BIT );

	// Get a handle to the parallel port's Data register
	ppDataHandle = mmap_device_io(PORT_LENGTH, DATA_ADDRESS );

	// Get a handle to the parallel port's Data register
	ppStatusHandle = mmap_device_io(PORT_LENGTH, STUS_ADDRESS );

	// Get a handle to the DIO control register
	dioControlHandle = mmap_device_io(PORT_LENGTH, DIO_CTRL_ADDRESS);

	// Get a handle to the DIO port channels
	adDataHandle = mmap_device_io(PORT_LENGTH, PORTA_ADDRESS);
	doneBitHandle = mmap_device_io(PORT_LENGTH, PORTB_ADDRESS);
	commStatusHandle = mmap_device_io(PORT_LENGTH, PORTC_ADDRESS);

	// Configure the DIO channels according to the protocol
	// A-output (A/D values), B-output (done bit), C-output (comm status)
	out8(dioControlHandle, 0x00);

	// We assume the communication link is enabled to start - so flag it as up
	out8(commStatusHandle, COMM_STATE_UP);

	// Set up an interrupt handler for the parallel port (ready signal in protocol)
	event.sigev_notify = SIGEV_INTR;
	intrId = InterruptAttachEvent(PARALLEL_IRQ, &event, 0);
	if (intrId == -1)
	{
		printf("Couldn't attach event to IRQ %d\n", PARALLEL_IRQ );
	}

	// Initialize the A/D register
	InitADRegister();

	// Configure the ready signal timeout parameters
	SIGEV_INTR_INIT(&timerEvent);
	timeoutSetTime = ONE_SECOND;
	timeoutRemTime = 0;

	// Loop indefinitely
	for(;;)
	{
		// Set a timeout for the interrupt
		TimerTimeout(CLOCK_REALTIME, _NTO_TIMEOUT_INTR, NULL, &timeoutSetTime, &timeoutRemTime);

		// Sleep until the next interrupt
		InterruptWait(0, NULL);
		out8(doneBitHandle, 0x00);

		// Check to see if the interrupt timed-out
		if(errno == ETIMEDOUT)
		{
			// Signal communication timeout
			out8(commStatusHandle, COMM_STATE_DOWN);
			errno = 0;
		}
		else
		{
			// Perform the A/D conversion
			PerformADConversion();
			adReturnValue = ReadADData();
			voltageLevel = ConvertADData(adReturnValue);

			// Check the voltage levels to see if we're in range (else alert the user!)
			if (voltageLevel < MIN_VOLTAGE_VAL || voltageLevel > MAX_VOLTAGE_VAL)
			{
				out8(commStatusHandle, VOLTAGE_RANGE_WARNING);
			}
			else
			{
				out8(adDataHandle, (voltageLevel * CONVERSION_FACTOR));
				out8(doneBitHandle, 0xFF);
				out8(commStatusHandle, COMM_STATE_UP);
			}
		}

		// Re-enable this interrupt
		InterruptUnmask(PARALLEL_IRQ, intrId);
	}

	// Remove the attachment from this thread
	InterruptDetach(intrId);

	// We should never get here
	return EXIT_SUCCESS;
}
